package com.yuanqi.service;

import com.yuanqi.domain.LoginAudit;
import com.yuanqi.domain.User;
import com.yuanqi.repository.LoginAuditRepository;
import com.yuanqi.repository.UserRepository;
import com.yuanqi.util.JwtUtil;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Service
public class AuthService {

    private final UserRepository userRepository;
    private final LoginAuditRepository loginAuditRepository;
    private final JwtUtil jwtUtil;
    private final BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    private final Argon2PasswordEncoder argon2PasswordEncoder = new Argon2PasswordEncoder();

    public AuthService(UserRepository userRepository,
                       LoginAuditRepository loginAuditRepository,
                       JwtUtil jwtUtil) {
        this.userRepository = userRepository;
        this.loginAuditRepository = loginAuditRepository;
        this.jwtUtil = jwtUtil;
    }

    public Optional<String> login(String username, String password, HttpServletRequest request) {
        Optional<User> userOpt = userRepository.findByUsername(username);
        boolean success = false;
        Long userId = null;
        String message;
        try {
            if (userOpt.isEmpty()) {
                message = "用户不存在";
                return Optional.empty();
            }
            User user = userOpt.get();
            userId = user.getId();
            if (user.getStatus() != null && user.getStatus() != (byte)1) {
                message = "用户已禁用或锁定";
                return Optional.empty();
            }
            if (!verifyPassword(user, password)) {
                message = "密码错误";
                return Optional.empty();
            }
            Map<String, Object> claims = new HashMap<>();
            claims.put("userId", user.getId());
            claims.put("username", user.getUsername());
            String token = jwtUtil.generateToken(user.getUsername(), claims);
            success = true;
            message = "登录成功";
            return Optional.of(token);
        } finally {
            // 审计日志
            LoginAudit audit = new LoginAudit();
            audit.setUserId(userId);
            audit.setUsername(username);
            audit.setSuccess(success ? (byte)1 : (byte)0);
            audit.setClientIp(request.getRemoteAddr());
            audit.setUserAgent(request.getHeader("User-Agent"));
            audit.setMessage(success ? "登录成功" : "登录失败");
            audit.setCreatedAt(LocalDateTime.now());
            loginAuditRepository.save(audit);
        }
    }

    private boolean verifyPassword(User user, String rawPassword) {
        String stored = user.getPasswordHash();
        if (stored == null || stored.isEmpty()) {
            return false;
        }
        String lower = stored.toLowerCase();
        // BCrypt: $2a$ / $2b$ / $2y$
        if (stored.startsWith("$2a$") || stored.startsWith("$2b$") || stored.startsWith("$2y$")) {
            return bCryptPasswordEncoder.matches(rawPassword, stored);
        }
        // Argon2: $argon2id$ 或 $argon2i$
        if (lower.startsWith("$argon2")) {
            return argon2PasswordEncoder.matches(rawPassword, stored);
        }
        // 兜底1：MD5(salt+password)
        String salt = user.getSalt() == null ? "" : user.getSalt();
        String salted = salt + rawPassword;
        String md5Salted = DigestUtils.md5DigestAsHex(salted.getBytes(StandardCharsets.UTF_8));
        if (md5Salted.equalsIgnoreCase(stored)) {
            return true;
        }
        // 兜底2：若历史数据未使用盐，则尝试 MD5(password)
        String md5Plain = DigestUtils.md5DigestAsHex(rawPassword.getBytes(StandardCharsets.UTF_8));
        return md5Plain.equalsIgnoreCase(stored);
    }
}


